package com.tuanzi.loan.workflow.cmd;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.history.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Lists;
import com.tuanzi.loan.core.util.SpringContextHolder;
import com.tuanzi.loan.workflow.common.ActivityType;

import lombok.extern.slf4j.Slf4j;

/**
 * @author Ray
 * @date 2018/01/30
 */
@Slf4j
public class GenerateFlowImageCmd implements Command<InputStream> {

	private String instanceId = "";

	public GenerateFlowImageCmd(String instanceId) {
		this.instanceId = instanceId;
	}

	@Override
	public InputStream execute(CommandContext context) {
		RepositoryService repositoryService = context.getProcessEngineConfiguration().getRepositoryService();
		HistoryService historyService = context.getProcessEngineConfiguration().getHistoryService();
		InputStream imageStream = null;

		try {
			HistoricProcessInstanceQuery hisProcInstQuery = historyService.createHistoricProcessInstanceQuery();
			HistoricProcessInstance hisProcInst = hisProcInstQuery.processInstanceId(instanceId).singleResult();

			if (hisProcInst == null) {
				throw new RuntimeException("获取流程图异常!");
			} else {
				HistoricActivityInstanceQuery hisActInstQuery = historyService.createHistoricActivityInstanceQuery();
				List<HistoricActivityInstance> hisActInstList = hisActInstQuery.processInstanceId(instanceId).orderByHistoricActivityInstanceStartTime().asc().list();

				// 过滤被回退的活动
				List<HistoricActivityInstance> activityInstGoBack = new ArrayList<HistoricActivityInstance>();
				for (HistoricActivityInstance hisActInst : hisActInstList) {
					// 过滤开始、结束节点
					if (StringUtils.equals(ActivityType.START_EVENT.getValue(), hisActInst.getActivityType())) {
						continue;
					}
					if ("endEvent".equals(hisActInst.getActivityType())) {
						if (hisActInst != hisActInstList.get(hisActInstList.size() - 1)) {
							// 重做
							activityInstGoBack.add(hisActInst);
							for (HistoricActivityInstance tmp : hisActInstList) {
								if (tmp == hisActInst) {
									break;
								}
								// 清除该任务的前置分支节点
								if ("exclusiveGateway".equals(tmp.getActivityType()) && tmp.getEndTime().getTime() == hisActInst.getStartTime().getTime()) {
									activityInstGoBack.add(tmp);
								}
							}
						}
						continue;
					}
					if (hisActInst.getTaskId() == null) {
						continue;
					}

					// 判断该活动是否正常结束
					HistoricTaskInstanceQuery hisTaskInstQuery = historyService.createHistoricTaskInstanceQuery();
					HistoricTaskInstance hisTaskInst = hisTaskInstQuery.taskId(hisActInst.getTaskId()).singleResult();

					if (hisTaskInst.getDeleteReason() != null && hisTaskInst.getDeleteReason().indexOf("endevent") != -1) {
						// 重做后的活动
						Date linkDate = null;
						for (HistoricActivityInstance tmp : hisActInstList) {
							if (tmp.getActivityId().equals(hisTaskInst.getTaskDefinitionKey()) && hisTaskInst.getId().equals(tmp.getTaskId())) {
								activityInstGoBack.add(tmp);
								linkDate = tmp.getEndTime();
							}
							if (linkDate != null && linkDate.getTime() == tmp.getStartTime().getTime()) {
								// 重做节点后续的活动也要屏蔽掉
								activityInstGoBack.add(tmp);
								linkDate = tmp.getEndTime();
							}
						}
					}
					if (hisTaskInst != null && hisTaskInst.getEndTime() != null && hisTaskInst.getDeleteReason() != null && !hisTaskInst.getDeleteReason().equals("completed")
							&& hisTaskInst.getDeleteReason().indexOf("endevent") == -1) {
						// 该任务被回退了
						activityInstGoBack.add(hisActInst);
						// 清除相同的activityId
						for (int i = 0; i < hisActInstList.size(); ++i) {
							HistoricActivityInstance tmp = hisActInstList.get(i);
							if (tmp == hisActInst) {
								break;
							}
							// 只过滤hisActInst之前的节点
							if (tmp.getActivityId().equals(hisActInst.getActivityId())) {
								if (tmp.getStartTime().getTime() != hisActInst.getStartTime().getTime()) {
									// 这个Activity不是会签节点，会签节点开始时间一致，不需要清除，
									activityInstGoBack.add(tmp);
								}
							}
							// 清除该任务的前置分支节点/servicetask
							if ("exclusiveGateway".equals(tmp.getActivityType()) || "serviceTask".equals(tmp.getActivityType())) {
								if (hisActInstList.get(i + 1).getActivityId().equals(hisActInst.getActivityId())) {
									activityInstGoBack.add(tmp);
								}
							}
						}
					}
				}

				if (!activityInstGoBack.isEmpty()) {
					hisActInstList.removeAll(activityInstGoBack);
				}

				List<String> activitiIds = Lists.newArrayList();
				List<String> flowIds = Lists.newArrayList();
				ProcessDefinitionEntity procDefEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
						.getDeployedProcessDefinition(hisProcInst.getProcessDefinitionId());

				flowIds = this.getHighLightedFlows(procDefEntity, hisActInstList);// 取流程走过的线

				for (HistoricActivityInstance hai : hisActInstList) {
					activitiIds.add(hai.getActivityId());// 获取流程走过的节点
				}

				BpmnModel bpmnModel = repositoryService.getBpmnModel(hisProcInst.getProcessDefinitionId());
				ProcessEngine processEngine = SpringContextHolder.getBean(ProcessEngine.class);
				imageStream = context.getProcessEngineConfiguration().getProcessDiagramGenerator().generateDiagram(bpmnModel, "png", activitiIds, flowIds,
						processEngine.getProcessEngineConfiguration().getActivityFontName(), processEngine.getProcessEngineConfiguration().getLabelFontName(),
						processEngine.getProcessEngineConfiguration().getAnnotationFontName(), processEngine.getProcessEngineConfiguration().getClassLoader(), 1.0);
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		return imageStream;
	}

	/**
	 * 获取流程走过的节点
	 * 
	 * @param procDefEntity
	 *            流程定义实体
	 * @param hisActInstList
	 *            历史活动实例集合
	 * @return
	 */
	private List<String> getHighLightedFlows(ProcessDefinitionEntity procDefEntity, List<HistoricActivityInstance> hisActInstList) {

		List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
		for (int i = 0; i < hisActInstList.size() - 1; i++) { // 对历史流程节点进行遍历
			ActivityImpl activityImpl = procDefEntity.findActivity(hisActInstList.get(i).getActivityId());
			// 得到节点定义的详细信息
			List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();
			// 用以保存后需开始时间相同的节点
			ActivityImpl sameActivityImpl1 = procDefEntity.findActivity(hisActInstList.get(i + 1).getActivityId());
			// 将后面第一个节点放在时间相同节点的集合里
			sameStartTimeNodes.add(sameActivityImpl1);
			for (int j = i + 1; j < hisActInstList.size() - 1; j++) {
				HistoricActivityInstance activityImpl1 = hisActInstList.get(j);
				// 后续第一个节点
				HistoricActivityInstance activityImpl2 = hisActInstList.get(j + 1);
				// 后续第二个节点
				if (activityImpl1.getStartTime().equals(activityImpl2.getStartTime())) {
					// 如果第一个节点和第二个节点开始时间相同保存
					ActivityImpl sameActivityImpl2 = procDefEntity.findActivity(activityImpl2.getActivityId());
					sameStartTimeNodes.add(sameActivityImpl2);
				} else {
					// 有不相同跳出循环
					break;
				}
			}

			List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();

			// 取出节点的所有出去的线
			for (PvmTransition pvmTransition : pvmTransitions) {
				// 对所有的线进行遍历
				ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition.getDestination();
				// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
				if (sameStartTimeNodes.contains(pvmActivityImpl)) {
					highFlows.add(pvmTransition.getId());
				}
			}
		}

		return highFlows;
	}
}
